///PHILOTES Source Code.  (C)2012 PhiloLabs
#pragma once

#include "OgrePrerequisites.h"

#include "OgreSingleton.h"
#include "OgreString.h"
//#include "extras/Overlay.h"

#if OGRE_PROFILING == 1
#	define OgreProfile( a ) Ogre::Profile _OgreProfileInstance( (a) )
#	define OgreProfileBegin( a ) Ogre::Profiler::getSingleton().beginProfile( (a) )
#	define OgreProfileEnd( a ) Ogre::Profiler::getSingleton().endProfile( (a) )
#	define OgreProfileGroup( a, g ) Ogre::Profile _OgreProfileInstance( (a), (g) )
#	define OgreProfileBeginGroup( a, g ) Ogre::Profiler::getSingleton().beginProfile( (a), (g) )
#	define OgreProfileEndGroup( a, g ) Ogre::Profiler::getSingleton().endProfile( (a), (g) )
#else
#   define OgreProfile( a )
#   define OgreProfileBegin( a )
#   define OgreProfileEnd( a )
#	define OgreProfileGroup( a, g ) 
#	define OgreProfileBeginGroup( a, g ) 
#	define OgreProfileEndGroup( a, g ) 
#endif

namespace Ogre {
	
	
	enum ProfileGroupMask
	{
		/// User default profile
		OGREPROF_USER_DEFAULT = 0x00000001,
		/// All in-built Ogre profiling will match this mask
		OGREPROF_ALL = 0xFF000000,
		/// General processing
		OGREPROF_GENERAL = 0x80000000,
		/// Culling
		OGREPROF_CULLING = 0x40000000,
		/// Rendering
		OGREPROF_RENDERING = 0x20000000
	};

    
	class _OgreExport Profile : public MemAlloc_General {

        public:
            Profile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT);
            ~Profile();

        protected:
            /// The name of this profile
            String mName;
			/// The group ID
			uint32 mGroupID;
    };

    
    class _OgreExport Profiler : public Singleton<Profiler>, public MemAlloc_General {

        public:
            Profiler();
            ~Profiler();

            void setTimer(Timing::PhiloTimer* t);

            Timing::PhiloTimer* getTimer();

            void beginProfile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT);

            void endProfile(const String& profileName, uint32 groupID = (uint32)OGREPROF_USER_DEFAULT);

            void setEnabled(bool enabled);

            bool getEnabled() const;

            void enableProfile(const String& profileName);

            void disableProfile(const String& profileName);

			void setProfileGroupMask(uint32 mask) { mProfileMask = mask; }
			
			uint32 getProfileGroupMask() const { return mProfileMask; }

            bool watchForMax(const String& profileName);

            bool watchForMin(const String& profileName);

            bool watchForLimit(const String& profileName, Real limit, bool greaterThan = true);

            void logResults();

            void reset();

			enum DisplayMode
			{
				/// Display % frame usage on the overlay
				DISPLAY_PERCENTAGE,
				/// Display milliseconds on the overlay
				DISPLAY_MILLISECONDS
			};

			
			void setDisplayMode(DisplayMode d) { mDisplayMode = d; }
			
			DisplayMode getDisplayMode() const { return mDisplayMode; }

            void setUpdateDisplayFrequency(uint freq);

            uint getUpdateDisplayFrequency() const;

// 			void setOverlayDimensions(Real width, Real height);
// 
// 			void setOverlayPosition(Real left, Real top);
// 
// 			Real getOverlayWidth() const;
// 			Real getOverlayHeight() const;
// 			Real getOverlayLeft() const;
// 			Real getOverlayTop() const;

            static Profiler& getSingleton(void);
            
            static Profiler* getSingletonPtr(void);

        protected:

            void initialize();

//            void displayResults();

            void processFrameStats();

            void changeEnableState();

//             OverlayContainer* createContainer();
// 
//             OverlayElement* createTextArea(const String& name, Real width, Real height, Real top, Real left, 
//                                        uint fontSize, const String& caption, bool show = true);
// 
//             OverlayElement* createPanel(const String& name, Real width, Real height, Real top, Real left, 
//                                     const String& materialName, bool show = true);

            /// Represents an individual profile call
            struct ProfileInstance {

                /// The name of the profile
                String		name;

                /// The name of the parent, empty string if root
                String		parent;

                /// The time this profile was started
                ulong		currTime;

                /// Represents the total time of all child profiles to subtract
                /// from this profile
                ulong		accum;

                /// The hierarchical level of this profile, 0 being the root profile
                uint		hierarchicalLvl;
            };

            /// Represents the total timing information of a profile
            /// since profiles can be called more than once each frame
            struct ProfileFrame {
				
                /// The name of the profile
                String	name;

                /// The total time this profile has taken this frame
                ulong	frameTime;

                /// The number of times this profile was called this frame
                uint	calls;

                /// The hierarchical level of this profile, 0 being the main loop
                uint	hierarchicalLvl;

            };
			
            /// Represents a history of each profile during the duration of the app
            struct ProfileHistory {

                /// The name of the profile
                String	name;

                /// The current percentage of frame time this profile has taken
                Real	currentTimePercent; 
				/// The current frame time this profile has taken in milliseconds
				Real	currentTimeMillisecs;

                /// The maximum percentage of frame time this profile has taken
                Real	maxTimePercent; 
				/// The maximum frame time this profile has taken in milliseconds
				Real	maxTimeMillisecs; 

                /// The minimum percentage of frame time this profile has taken
                Real	minTimePercent; 
				/// The minimum frame time this profile has taken in milliseconds
				Real	minTimeMillisecs; 

                /// The number of times this profile has been called each frame
                uint	numCallsThisFrame;

                /// The total percentage of frame time this profile has taken
                Real	totalTimePercent;
				/// The total frame time this profile has taken in milliseconds
				Real	totalTimeMillisecs;

                /// The total number of times this profile was called
                /// (used to calculate average)
                ulong	totalCalls; 

                /// The hierarchical level of this profile, 0 being the root profile
                uint	hierarchicalLvl;

			};

			
            typedef list<ProfileInstance>::type ProfileStack;
            typedef list<ProfileFrame>::type ProfileFrameList;
            typedef list<ProfileHistory>::type ProfileHistoryList;
            typedef map<String, ProfileHistoryList::iterator>::type ProfileHistoryMap;
            typedef map<String, bool>::type DisabledProfileMap;

            //typedef list<OverlayElement*>::type ProfileBarList;

            /// A stack for each individual profile per frame
            ProfileStack mProfiles;

            /// Accumulates the results of each profile per frame (since a profile can be called
            /// more than once a frame)
            ProfileFrameList mProfileFrame;

            /// Keeps track of the statistics of each profile
            ProfileHistoryList mProfileHistory;

            /// We use this for quick look-ups of profiles in the history list
            ProfileHistoryMap mProfileHistoryMap;

            /// Holds the names of disabled profiles
            DisabledProfileMap mDisabledProfiles;

            /// Holds the display bars for each profile results
//            ProfileBarList mProfileBars;

            /// Whether the GUI elements have been initialized
            bool mInitialized;

            /// The max number of profiles we can display
            uint mMaxDisplayProfiles;

//             /// The overlay which contains our profiler results display
//             Overlay* mOverlay;
// 
//             /// The window that displays the profiler results
//             OverlayContainer* mProfileGui;

            /// The height of each bar
            Real mBarHeight;

            /// The height of the stats window
            Real mGuiHeight;

            /// The width of the stats window
            Real mGuiWidth;

			/// The horz position of the stats window
			Real mGuiLeft;

			/// The vertical position of the stats window
			Real mGuiTop;

			/// The size of the indent for each profile display bar
            Real mBarIndent;

            /// The width of the border between the profile window and each bar
            Real mGuiBorderWidth;

            /// The width of the min, avg, and max lines in a profile display
            Real mBarLineWidth;

			/// The distance between bars
			Real mBarSpacing;

            /// The number of frames that must elapse before the current
            /// frame display is updated
            uint mUpdateDisplayFrequency;

            /// The number of elapsed frame, used with mUpdateDisplayFrequency
            uint mCurrentFrame;

            /// The timer used for profiling
            Timing::PhiloTimer* mTimer;

            /// The total time each frame takes
            ulong mTotalFrameTime;

            /// Whether this profiler is enabled
            bool mEnabled;

            /// Keeps track of whether this profiler has
            /// received a request to be enabled/disabled
            bool mEnableStateChangePending;

            /// Keeps track of the new enabled/disabled state that the user has requested
            /// which will be applied after the frame ends
            bool mNewEnableState;

			/// Mask to decide whether a type of profile is enabled or not
			uint32 mProfileMask;

			/// How to display the overlay
			DisplayMode mDisplayMode;

			/// The max frame time recorded
			ulong mMaxTotalFrameTime;

			/// Rolling average of millisecs
			Real mAverageFrameTime;
			bool mResetExtents;


    }; // end class

} // end namespace

